package exebuilder.core;

import exebuilder.FileUtil;
import exebuilder.command.CommandUtils;
import exebuilder.controller.EndController;
import exebuilder.controller.MainController;
import exebuilder.data.AppConfig;
import exebuilder.ui.MessageBox;
import javafx.application.Platform;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;

public class AppBuilder {

  private static final Logger logger = LogManager.getLogger();

  String moduleDeps;

  String runtimePath;

  AppConfig appConfig;

  String finalOutputPath;

  MainController mainController;
  EndController endController;

  public AppBuilder() {
    initData();
  }

  private void initData() {
    appConfig = AppConfig.getInstance();
    if (StringUtils.isBlank(appConfig.getAppName())) {
      appConfig.setAppName("MyApplication");
    }
    this.finalOutputPath = appConfig.getOutputPath() + "\\" + appConfig.getAppName();
    this.mainController = (MainController) Controllers.get("main");
    this.endController = (EndController) Controllers.get("end");
  }

  public void build() {
    try {
      mainController.backButton.setDisable(true);
      mainController.nextButton.setDisable(true);
      endController.buildInfoArea.clear();
      logger.debug("开始检测运行环境");
      initJavaEnv();
      logger.debug("开始创建输出目录");
      createOutputDir();
      logger.debug("开始分析运行依赖");
      analyzeDependencies();
      logger.debug("开始创建运行时");
      createRuntime();
      logger.debug("开始应用程序镜像");
      createAppImage();

    } catch (Exception e) {
      logger.error("command error:", e);
      MessageBox.error(e.getMessage());
    } finally {
      mainController.backButton.setDisable(false);
      mainController.nextButton.setDisable(false);
    }

  }

  private void initJavaEnv() throws Exception {
    ArrayList<String> commandLine = new ArrayList<>();
    commandLine.add("-version");
    String version = CommandUtils.execute(new File(appConfig.getJdkPath(), "bin"), "java", commandLine).trim();
    logger.debug("java version:" + version);
  }

  private void createOutputDir() throws IOException {
    var outputDir = Paths.get(finalOutputPath);
    if (Files.exists(outputDir)) {
      FileUtil.deleteDir(finalOutputPath);
    }
    try {
      Files.createDirectory(outputDir);
      logger.debug("输出目录创建完毕");
      Platform.runLater(() -> endController.buildInfoArea.appendText(String.format("输出目录：%s 创建成功\n", finalOutputPath)));
    } catch (IOException e) {
      logger.error("创建目录失败：", e);
      Platform.runLater(() -> endController.buildInfoArea.appendText(String.format("创建目录失败：%s\n", e.getMessage())));
      throw e;
    }

  }

  private void analyzeDependencies() throws Exception {
    var mainJarPath = appConfig.getSourcePath() + "\\" + appConfig.getMainJarFile();
    var libPath = appConfig.getLibPath();
    ArrayList<String> commandLine = new ArrayList<>();
    commandLine.add("--multi-release");
    commandLine.add(appConfig.getJdkVersion().toString());
    commandLine.add("-q");
    commandLine.add("--print-module-deps");
    commandLine.add("--ignore-missing-deps");
    commandLine.add(mainJarPath);
    if (!StringUtils.isBlank(libPath)) {
      commandLine.add(libPath + "\\*.jar");
    }
    moduleDeps = CommandUtils.execute(new File(appConfig.getJdkPath(), "bin"), "jdeps", commandLine).trim();
    Platform.runLater(() -> endController.buildInfoArea.appendText(String.format("查找依赖模块：%s\n", moduleDeps)));
    logger.debug("moduleDeps：{}", moduleDeps);
  }

  private void createRuntime() throws Exception {
    var runtimePath = finalOutputPath + "\\runtime";
    var runtimeDir = Paths.get(runtimePath);
    if (Files.exists(runtimeDir)) {
      FileUtil.deleteDir(runtimePath);
    }
    ArrayList<String> commandLine = new ArrayList<>();
    commandLine.add("--add-modules");
    commandLine.add(this.moduleDeps);
    commandLine.add("--no-header-files");
    commandLine.add("--no-man-pages");
    commandLine.add("--compress");
    commandLine.add("2");
    commandLine.add("--output");
    commandLine.add(runtimePath);
    String out = CommandUtils.execute(new File(appConfig.getJdkPath(), "bin"), "jlink", commandLine);
    if (StringUtils.isBlank(out)) {
      logger.info("运行时镜像创建成功");
      Platform.runLater(() -> endController.buildInfoArea.appendText(String.format("运行时镜像：%s 创建成功\n", runtimePath)));
      this.runtimePath = runtimePath;
    } else {
      logger.error("运行时镜像创建失败：{}", out);
      Platform.runLater(() -> endController.buildInfoArea.appendText(String.format("运行时镜像创建失败： %s\n", out)));
    }
  }

  private void createAppImage() throws Exception {
    String imageOutputPath = finalOutputPath + "\\" + appConfig.getAppName() + "_unpack";
    if (Files.exists(Paths.get(imageOutputPath))) {
      FileUtil.deleteDir(imageOutputPath);
    }
    ArrayList<String> commandLine = new ArrayList<>();
    commandLine.add("--type");
    commandLine.add("app-image");
    commandLine.add("-i");
    commandLine.add(appConfig.getSourcePath());
    commandLine.add("-n");
    commandLine.add(appConfig.getAppName());
    commandLine.add("--main-jar");
    commandLine.add(".\\" + appConfig.getMainJarFile());
    commandLine.add("--runtime-image");
    commandLine.add(runtimePath);

    if (!StringUtils.isBlank(appConfig.getVersion())) {
      commandLine.add("--app-version");
      commandLine.add(appConfig.getVersion());
    }

    if (!StringUtils.isBlank(appConfig.getIconPath())) {
      commandLine.add("--icon");
      commandLine.add(appConfig.getIconPath());
    }
    if (!StringUtils.isBlank(appConfig.getCopyright())) {
      commandLine.add("--copyright");
      commandLine.add(appConfig.getCopyright());
    }
    commandLine.add("--dest");
    commandLine.add(imageOutputPath);
    if (appConfig.getAppType() == 1) {
      commandLine.add("--win-console");
    }
    CommandUtils.execute(new File(appConfig.getJdkPath(), "bin"), "jpackage", commandLine);
    deleteRuntimeDir();
    logger.info("执行完毕");
    Platform.runLater(() -> endController.buildInfoArea.appendText(String.format("可执行文件：%s\\%s.exe 创建成功\n", imageOutputPath, appConfig.getAppName())));
    Platform.runLater(() -> endController.buildInfoArea.appendText("执行完毕\n"));
  }

  private void deleteRuntimeDir() throws IOException {
    String runtimePath = finalOutputPath + "\\runtime";
    Path runtimeDir = Paths.get(runtimePath);
    if (Files.exists(runtimeDir)) {
      FileUtil.deleteDir(runtimePath);
    }
  }
}
